# coding = utf-8
# File date: Hi_2022/3/11 0:35
# File_name: DriverDownload.py
from concurrent.futures import ThreadPoolExecutor
from os import mkdir, walk, remove
from os.path import exists, abspath, join, isfile
import requests
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from zipfile import ZipFile


class Driver_Download:
    def __init__(self, mirror_url, driver_folder="win32.zip"):
        """初始化
        driver_folder下载驱动类型可选linux64.zip、mac64.zip、mac64_m1.zip、win32.zip、mac32.zip、linux32.zip等
        """
        if isfile("./all_version_list.txt"):  # 删除遗留配置文件
            print(f"删除:{abspath('./all_version_list.txt')}")
            remove("./all_version_list.txt")

        if isfile("./driver_download_list.txt"):  # 删除遗留配置文件
            print(f"删除:{abspath('./driver_download_list.txt')}")
            remove("./driver_download_list.txt")

        self.url = mirror_url
        self.driver_folder = driver_folder

        self.driver = Driver_Download.start_chrome()  # 谷歌
        # self.driver = Driver_Download.start_phantomjs()  # phantomjs

        self.driver.implicitly_wait(1)  # 隐式等待

    @staticmethod
    def start_chrome():
        """谷歌无头浏览器模式，需要先有一个基本可用驱动"""
        chrome_options = Options()
        chrome_options.add_argument('--headless')
        chrome_options.add_argument('--disable-gpu')
        driver = webdriver.Chrome(options=chrome_options)
        return driver

    @staticmethod
    def start_phantomjs():
        """phantomjs无头，尝试selenium3.0.1版本正常无警告"""
        phantomjs_path = "./phantomjs-2.1.1-windows/bin/phantomjs.exe"
        driver = webdriver.PhantomJS(executable_path=phantomjs_path)
        return driver

    def get_all_version(self):
        """获取所有版本列表并写入,all_version_list.txt.生成后需要手动删除无效内容"""
        self.driver.get(self.url)

        # 去除匹配到文本包含LATEST_RELEASE、icons、Parent、index、Parent、Name的元素
        xpathstr = '//a[contains(text(), "/") and not(contains(text(), "icon"))]'
        all_version_list = WebDriverWait(self.driver, timeout=5, poll_frequency=0.3).until(
            lambda x: x.find_elements(By.XPATH, xpathstr))
        # all_version_list = self.driver.find_elements(By.XPATH, xpathstr)

        # 将解析到的所有内容写入文件
        for version in all_version_list:
            text = version.text
            url = version.get_attribute("href")
            with open("./all_version_list.txt", mode="a", encoding="utf-8") as f:  # 去掉版本号后面多余的/
                f.writelines(f"{text.replace('/', '')}==={url}\n")

    def check_directory(self):
        """
        读取all_version_list.txt并创建相应目录
        """
        print("准备创建所有目录")
        if not exists(f"./{self.driver_folder}"):
            mkdir(f"./{self.driver_folder}")  # 创建目录
            print(f"创建根目录：{self.driver_folder}")
        with open("./all_version_list.txt", mode="r", encoding="utf-8") as f:
            for version_line in f.readlines():  # 检查每个版本文件目录创建
                version = version_line.split("===")[0]
                if not exists(f"./{self.driver_folder}/{version}"):
                    mkdir(f"./{self.driver_folder}/{version}")
                    print(f'创建目录：{abspath(f"./{self.driver_folder}/{version}")}')
                else:
                    print(f'已存在目录：{abspath(f"./{self.driver_folder}/{version}")}')

    def get_version_address(self, url):
        """读取指定版本url地址，返回指定类型驱动名称和下载地址"""
        print(f"准备读取：{url}")
        self.driver.get(url)
        specify_version = self.driver.find_elements(By.XPATH, f'//a[contains(text(), "{self.driver_folder}")]')
        if specify_version:
            return specify_version[0].text, specify_version[0].get_attribute("href")
        else:
            return False

    def get_download_list(self):
        """读取all_version_list.txt,并将指定系统镜像和版本写入driver_download_list.txt"""
        # 读取总版本列表
        with open("./all_version_list.txt", mode="r", encoding="utf-8") as f:
            with ThreadPoolExecutor(max_workers=10) as pool:  # 创建一个最大容纳数量为10的线程池
                for version_url in f.readlines():
                    version, url = version_url.split("===")
                    specify_version = pool.submit(self.get_version_address, url)  # 能找到则返回一个元组(name, url)
                    # 根据返回结果，将完整驱动名称和下载地址写入driver_download_list。txt
                    specify_version = specify_version.result()
                    if specify_version:
                        with open("./driver_download_list.txt", mode="a", encoding="utf-8") as f:
                            f.write(
                                f"{abspath(f'./{self.driver_folder}')}\\{version}\\{specify_version[0]}==={specify_version[1]}\n")
                pool.shutdown()

    @staticmethod
    def download(path, url):
        """下载方法，path为包含文件名的文件路径，需要带上后缀名如.zip"""
        print(f"准备下载 {url}")
        with open(path, mode="wb") as f:
            f.write(requests.get(url).content)

    @staticmethod
    def down_pool_main():
        """创建最大5的线程执行driver_download_list.txt所有下载任务"""
        with open("./driver_download_list.txt", mode="r", encoding="utf-8") as file:
            with ThreadPoolExecutor(max_workers=10) as pool:  # 创建一个最大容纳数量为10的线程池
                for version_line in file.readlines():
                    path, url = version_line.split("===")
                    url = url.strip("\n")  # 去除多于换行符
                    pool.submit(Driver_Download.download, path, url)  # 把所有线程提交到线程池
                pool.shutdown(wait=True)

    def unzip_delzip(self):
        """遍历self.driver_folder. 并解压后删除zip文件"""
        print(f"\n准备解压并删除所有驱动压缩包\n")
        for root_path, all_dirs, all_files in walk(abspath(f"./{self.driver_folder}")):  # 遍历整个下载文件的文件件
            for file in all_files:
                file_full_path = join(root_path, file)
                with ZipFile(file_full_path, mode="r") as zip_file:  # 解压文件
                    zip_file.extractall(root_path)
                    print(f"解压：{file_full_path}")
                remove(file_full_path)
                print(f"删除：{file_full_path}")


if __name__ == '__main__':
    # mirror_url = "https://registry.npmmirror.com/binary.html?path=chromedriver"  # 镜像下载地址
    mirror_url = "https://registry.npmmirror.com/-/binary/chromedriver"  # 镜像下载地址
    # driver_folder下载驱动类型可选linux64.zip、mac64.zip、mac64_m1.zip、win32.zip、mac32.zip、linux32.zip等
    t = Driver_Download(driver_folder="win32.zip", mirror_url=mirror_url)
    t.get_all_version()  # 获取所有版本列表
    t.check_directory()  # 检查所有版本目录
    t.get_download_list()  # 获取所有下载文件地址
    t.down_pool_main()  # 下载所有版本文件
    t.driver.quit()
    t.unzip_delzip()  # 解压并删除
